﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Zo.Caching
{
    /// <summary>
    /// 默认缓存管理器。
    /// </summary>
    public class DefaultCacheManager : ICacheManager
    {
        /// <summary>
        /// 静态字典缓存数据。
        /// </summary>
        private readonly IDictionary<string, CacheEntity> _cache = new Dictionary<string, CacheEntity>();

        /// <summary>
        /// 锁定对象。
        /// </summary>
        private static readonly object _lockObject = new object();

        /// <summary>
        /// 获得所有 Key。
        /// </summary>
        public IEnumerable<string> Keys => _cache.Keys;

        public bool Add<TValue>(string key, TValue value)
        {
            lock (_lockObject)
            {
                if (_cache.ContainsKey(key))
                    return false;

                _cache.Add(key, new CacheEntity
                {
                    Value = value,
                    ExpiredTime = DateTime.UtcNow.AddYears(1)
                });

                return true;
            }
        }

        /// <summary>
        /// 获取缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <returns>值对象</returns>
        public TValue Get<TValue>(string key)
        {
            CacheEntity cache = null;

            lock (_lockObject)
            {
                if (_cache.ContainsKey(key))
                    cache = _cache[key];
            }

            return cache != null && cache.ExpiredTime > DateTime.UtcNow ? (TValue)cache.Value : default(TValue);
        }

        /// <summary>
        /// 获取缓存值，如果不存在，则添加缓存。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="acquire">获得值对象的委托。</param>
        /// <returns>值对象。</returns>
        public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire)
        {
            lock (_lockObject)
            {
                var result = Get<TValue>(key);

                if (result != null)
                    return result;

                result = acquire();

                if (result != null)
                {
                    var cache = new CacheEntity
                    {
                        Value = result,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[key] = cache;
                }

                return result;
            }
        }

        /// <summary>
        /// 获取缓存值，如果不存在，则添加缓存。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="acquire">获得值对象的委托。</param>
        /// <param name="validFor"></param>
        /// <returns>值对象。</returns>
        public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire, TimeSpan validFor)
        {
            lock (_lockObject)
            {
                var result = Get<TValue>(key);

                if (result != null)
                    return result;

                result = acquire();

                if (result != null)
                {
                    var cache = new CacheEntity
                    {
                        Value = result,
                        ExpiredTime = DateTime.UtcNow.Add(validFor)
                    };

                    _cache[key] = cache;
                }

                return result;
            }
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="value">值对象</param>
        public void Set<TValue>(string key, TValue value)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

            var cache = new CacheEntity
            {
                Value = value,
                ExpiredTime = DateTime.UtcNow.AddYears(1)
            };

            lock (_lockObject)
            {
                _cache[key] = cache;
            }
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="value">值对象。</param>
        /// <param name="validFor">有效时长。</param>
        /// <exception cref="ArgumentNullException">key 不可以是空的。</exception>
        public void Set<TValue>(string key, TValue value, TimeSpan validFor)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

            var cache = new CacheEntity
            {
                Value = value,
                ExpiredTime = DateTime.UtcNow.Add(validFor)
            };

            lock (_lockObject)
            {
                var cacheKeys = _cache
                    .Where(item => item.Value.ExpiredTime < DateTime.Now)
                    .Select(item => item.Key);

                if (cacheKeys.Any())
                {
                    foreach (var cacheKey in cacheKeys)
                    {
                        _cache.Remove(cacheKey);
                    }
                }

                _cache[key] = cache;
            }
        }

        /// <summary>
        /// 使用并发锁设置缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="action">缓存值修改方法。</param>
        /// <param name="timeOut">缓存的有效时长。</param>
        /// <param name="lockTimeOut">锁的有效时长。</param>
        public void SetLock<TValue>(string key, Func<TValue, TValue> action, TimeSpan timeOut, TimeSpan lockTimeOut)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException(nameof(key), "键对象不可以是空的");

            if (action == null)
                throw new ArgumentNullException(nameof(action), "设置方法不可以是空的");

            lock (_lockObject)
            {
                var value = Get<TValue>(key);

                value = action(value);

                var cache = new CacheEntity
                {
                    Value = value,
                    ExpiredTime = DateTime.UtcNow.Add(timeOut)
                };

                _cache[key] = cache;
            }
        }

        /// <summary>
        /// 移除缓存值。
        /// </summary>
        /// <param name="key">键对象。</param>
        public bool Remove(string key)
        {
            lock (_lockObject)
            {
                return _cache.Remove(key);
            }
        }

        /// <summary>
        /// 移除缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="result">值对象</param>
        /// <returns></returns>
        public bool Remove<TValue>(string key, out TValue result)
        {
            result = default(TValue);

            lock (_lockObject)
            {
                if (_cache.ContainsKey(key))
                {
                    result = (TValue)_cache[key].Value;
                    return _cache.Remove(key);
                }

                return false;
            }
        }

        /// <summary>
        /// 清空缓存。
        /// </summary>
        public void Clear()
        {
            _cache.Clear();
        }

        #region 数值操作

        /// <summary>
        /// 根据指定的Key，将值加1(仅整型有效)。
        /// </summary>
        /// <param name="key"></param>
        public void IncrementValue(string key)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                if (!_cache.ContainsKey(key))
                {
                    cache = new CacheEntity
                    {
                        Value = 1,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[key] = cache;
                }
                else
                {
                    cache = _cache[key];

                    var value = Convert.ToInt32(cache.Value);

                    cache.Value = ++value;

                    _cache[key] = cache;
                }
            }
        }

        /// <summary>
        /// 根据指定的Key，将值加上指定值(仅整型有效)。
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void IncrementValueBy(string key, int value)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                if (!_cache.ContainsKey(key))
                {
                    cache = new CacheEntity
                    {
                        Value = value,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[key] = cache;
                }
                else
                {
                    cache = _cache[key];

                    cache.Value = Convert.ToInt32(cache.Value) + value;

                    _cache[key] = cache;
                }
            }
        }

        /// <summary>
        /// 根据指定的Key，将值减1(仅整型有效)。
        /// </summary>
        /// <param name="key"></param>
        public void DecrementValue(string key)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                if (!_cache.ContainsKey(key))
                {
                    cache = new CacheEntity
                    {
                        Value = -1,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[key] = cache;
                }
                else
                {
                    cache = _cache[key];

                    var value = Convert.ToInt32(cache.Value);

                    cache.Value = --value;

                    _cache[key] = cache;
                }
            }
        }

        /// <summary>
        /// 根据指定的Key，将值减去指定值(仅整型有效)。
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void DecrementValueBy(string key, int value)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                if (!_cache.ContainsKey(key))
                {
                    cache = new CacheEntity
                    {
                        Value = -value,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[key] = cache;
                }
                else
                {
                    cache = _cache[key];

                    cache.Value = Convert.ToInt32(cache.Value) - value;

                    _cache[key] = cache;
                }
            }
        }

        #endregion 数值操作

        #region 集合操作

        /// <summary>
        /// 添加一个项到内部的List<T>。
        /// </summary>
        /// <param name="listId"></param>
        /// <param name="value"></param>
        public void AddItemToList(string listId, string value)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                var list = new List<string>();

                if (!_cache.ContainsKey(listId))
                {
                    list.Add(value);

                    cache = new CacheEntity
                    {
                        Value = list,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[listId] = cache;
                }
                else
                {
                    cache = _cache[listId];

                    list = (List<string>)cache.Value;

                    list.Add(value);

                    cache.Value = list;

                    _cache[listId] = cache;
                }
            }
        }

        /// <summary>
        /// 一次过将参数中的List<T>中的多个值添加入内部的List<T>。
        /// </summary>
        /// <param name="listId"></param>
        /// <param name="value"></param>
        public void AddRangeToList(string listId, IEnumerable<string> value)
        {
            lock (_lockObject)
            {
                CacheEntity cache = null;

                var list = new List<string>();

                if (!_cache.ContainsKey(listId))
                {
                    list.AddRange(value);

                    cache = new CacheEntity
                    {
                        Value = list,
                        ExpiredTime = DateTime.UtcNow.AddYears(1)
                    };

                    _cache[listId] = cache;
                }
                else
                {
                    cache = _cache[listId];

                    list = (List<string>)cache.Value;

                    list.AddRange(value);

                    cache.Value = list;

                    _cache[listId] = cache;
                }
            }
        }

        /// <summary>
        /// 移除指定ListId的内部List<T>中第二个参数值相等的那一项。
        /// </summary>
        /// <param name="listId"></param>
        /// <param name="value"></param>
        public void RemoveItemFromList(string listId, string value)
        {
            lock (_lockObject)
            {
                if (_cache.ContainsKey(listId))
                {
                    var cache = _cache[listId];

                    var list = (List<string>)cache.Value;

                    var status = list.Remove(value);

                    if (status)
                    {
                        cache.Value = list;

                        _cache[listId] = cache;
                    }
                }
            }
        }

        /// <summary>
        /// 获取指定ListId的内部List<T>的所有值。
        /// </summary>
        /// <param name="listId"></param>
        /// <returns></returns>
        public IEnumerable<string> GetAllItemsFromList(string listId)
        {
            lock (_lockObject)
            {
                if (_cache.ContainsKey(listId))
                {
                    var cache = _cache[listId];

                    return (List<string>)cache.Value;
                }

                return null;
            }
        }

        #endregion 集合操作

        /// <summary>
        /// 缓存实体。
        /// </summary>
        private class CacheEntity
        {
            /// <summary>
            /// 缓存值。
            /// </summary>
            public object Value { get; set; }

            /// <summary>
            /// 过期时间（UTC）
            /// </summary>
            public DateTime ExpiredTime { get; set; }
        }

        /// <summary>
        /// 参与者状态。
        /// </summary>
        private struct EventStatus
        {
            /// <summary>
            /// 空闲。
            /// </summary>
            public const int Idle = 0;

            /// <summary>
            /// 忙碌。
            /// </summary>
            public const int Busy = 1;

            /// <summary>
            /// 退出。
            /// </summary>
            public const int Exit = 2;
        }
    }
}